package com.cathyjava.easyflows.workflow;

import com.cathyjava.easyflows.work.Work;
import com.cathyjava.easyflows.work.WorkContext;
import com.cathyjava.easyflows.work.WorkPredicate;
import com.cathyjava.easyflows.work.WorkReport;
import com.cathyjava.easyflows.work.impl.NoOpWork;

/**
 * @author chenjing
 * @date 2021/5/8
 */
public class ConditionalWorkFlow extends AbstractWorkFlow {
    private String name;
    private WorkPredicate predicate;
    private Work initWork, onPredicateSuccessWork, onPredicateFailWork;

    private ConditionalWorkFlow(String name, WorkPredicate predicate, Work initWork, Work onPredicateSuccessWork, Work onPredicateFailWork) {
        super(name);
        this.predicate = predicate;
        this.initWork = initWork;
        this.onPredicateSuccessWork = onPredicateSuccessWork;
        this.onPredicateFailWork = onPredicateFailWork;
    }

    @Override
    public WorkReport execute(WorkContext workContext) {
        WorkReport workReport = this.initWork.execute(workContext);
        if (predicate.apply(workReport)) {
            onPredicateSuccessWork.execute(workContext);
        } else {
            if (onPredicateFailWork != null && !(onPredicateFailWork instanceof NoOpWork)) {
                onPredicateFailWork.execute(workContext);
            }
        }
        return workReport;
    }

    public static class Builder {
        private Builder() {
        }

        public static NamedStep aNewConditionalFlow() {
            return new BuilderStep();
        }

        public interface ExecuteStep {
            WhenStep execute(Work initWork);
        }

        public interface NamedStep {
            ExecuteStep named(String name);
        }

        public interface WhenStep {
            ThenStep when(WorkPredicate predicate);
        }

        public interface ThenStep {
            OtherwiseStep then(Work work);
        }

        public interface OtherwiseStep extends BuildStep {
            BuildStep otherwise(Work work);
        }

        public interface BuildStep {
            ConditionalWorkFlow build();
        }

        private static class BuilderStep implements NamedStep, ExecuteStep, WhenStep, ThenStep, OtherwiseStep, BuildStep {
            private String name;
            private WorkPredicate predicate;
            private Work initWork, onPredicateSuccessWork, onPredicateFailWork;

            @Override
            public WhenStep execute(Work initWork) {
                this.initWork = initWork;
                return this;
            }

            @Override
            public ExecuteStep named(String name) {
                this.name = name;
                return this;
            }

            @Override
            public ThenStep when(WorkPredicate predicate) {
                this.predicate = predicate;
                return this;
            }

            @Override
            public OtherwiseStep then(Work work) {
                this.onPredicateSuccessWork = work;
                return this;
            }

            @Override
            public BuildStep otherwise(Work work) {
                this.onPredicateFailWork = work;
                return this;
            }

            @Override
            public ConditionalWorkFlow build() {
                return new ConditionalWorkFlow(name, predicate, initWork, onPredicateSuccessWork, onPredicateFailWork);
            }
        }
    }
}
